package tech.heiming.aps.scheduler.deadline;

import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.api.score.stream.Constraint;
import org.optaplanner.core.api.score.stream.ConstraintFactory;
import org.optaplanner.core.api.score.stream.ConstraintProvider;
import org.optaplanner.core.api.score.stream.Joiners;

import java.time.LocalDateTime;

/**
 * @author heimingz
 */
public class ScheduleTableConstraintProvider implements ConstraintProvider {
    private final int LIMIT = 1;

    @Override
    public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
        return new Constraint[]{
                // Hard constraints
                ShiftCraftTimeConflict(constraintFactory),
                IdConflict(constraintFactory),
                CraftGapConflict(constraintFactory),
                ResourceTimeConflict(constraintFactory),
                OutOfTimeConflict(constraintFactory)
                //SerialTimeConflict(constraintFactory),
        };
    }

    //上班时间不能和工艺时间冲突
    private Constraint ShiftCraftTimeConflict(ConstraintFactory constraintFactory) {
        return constraintFactory.from(OrderSlice.class).filter(OrderSlice::shiftCraftTimeConflict)
                .penalize("ShiftCraftTimeConflict", HardSoftScore.ONE_HARD);
        //return null;
    }

    //工艺必须按照顺序来
    private Constraint IdConflict(ConstraintFactory constraintFactory) {
        return constraintFactory.from(OrderSlice.class).join(
                OrderSlice.class)
                .filter((p1,p2)->{
                    if (p1.getId().equals(p2.getId())) return false;
                    if (!p1.getOrderId().equals(p2.getOrderId())) return false;
                    if (p1.getItemIndex().equals(p2.getItemIndex())){
                        if (p1.getCraftIndex()>p2.getCraftIndex()){
                            if (p1.getStartTimeslot()<p2.getStartTimeslot())
                                return true;
                            else
                                return false;
                        }
                    }else{
                        if (p1.isLastCraft()&&p2.isLastCraft()){
                            if (p1.getItemIndex()>p2.getItemIndex()){
                                if (p1.getStartTimeslot()<p2.getStartTimeslot()) return true;
                                return false;
                            }
                        }
                    }
                    return false;
                })
                .penalize("IdConflict",HardSoftScore.ONE_HARD);
        //return null;
    }

    //同订单不同工艺 或者 不同订单 必须有 0.1H 间隔
    private Constraint CraftGapConflict(ConstraintFactory constraintFactory) {
        return constraintFactory.from(OrderSlice.class).join(
                OrderSlice.class)
                .filter((p1,p2)->{
                            if (p1.getId().equals(p2.getId())) return false;
                            //如果生产的是同一个物品,而且是同一个工艺步骤,直接返回false
                            if (p1.getProduct().getProductId().equals(p2.getProduct().getProductId())
                                    && p1.getCraftIndex().equals(p2.getCraftIndex())
                            ){
                                return false;
                            }

                            boolean pd = false;
                            if (p1.getEquipments()!=null&&p2.getEquipments()!=null&&!p1.getEquipments().getGroupId().equals("null")&&!p2.getEquipments().getGroupId().equals("null")){
                                if (p1.getEquipments().getGroupId().equals(p2.getEquipments().getGroupId())){
                                    pd=true;
                                }
                            }
                            if (p1.getEmployees()!=null&&p2.getEmployees()!=null){
                                for (int i=0;i<p1.getEmployees().getEmployeeGroups().size();i++){
                                    for (int j=0;j<p2.getEmployees().getEmployeeGroups().size();j++){
                                        if (p1.getEmployees().getEmployeeGroups().get(i).getAmount() == 0) continue;
                                        if (p2.getEmployees().getEmployeeGroups().get(j).getAmount() == 0) continue;
                                        if (p1.getEmployees().getEmployeeGroups().get(i).getResourceId().equals(p2.getEmployees().getEmployeeGroups().get(j).getResourceId())){
                                            pd = true;
                                            break;
                                        }
                                    }
                                    if (pd){
                                        break;
                                    }
                                }
                            }

                            if (pd){
                                Long minTimeEnd= -1L;
                                Long maxTimeStart=-1L;
                                if (p1.getStartTimeslot() + LIMIT - 1 > p2.getStartTimeslot() + LIMIT - 1){
                                    minTimeEnd = p2.getStartTimeslot() + LIMIT - 1;
                                    maxTimeStart = p1.getStartTimeslot();
                                }else{
                                    minTimeEnd = p1.getStartTimeslot() + LIMIT - 1;
                                    maxTimeStart = p2.getStartTimeslot();
                                }
                                if (maxTimeStart - minTimeEnd < 0){//修改为无间隔 或者 0.1H 间隔
                                    return true;
                                }else{
                                    return false;
                                }

                            }else{
                                return false;
                            }

                        }
                )
                .penalize("CraftGapConflict",HardSoftScore.ONE_HARD);
    }

    //一个资源只能属于一个时间片 不能出现交叉
    private Constraint ResourceTimeConflict(ConstraintFactory constraintFactory) {
        return constraintFactory.from(OrderSlice.class).join(
                OrderSlice.class)
                .filter((p1,p2)->{
                    if (p1.getId().equals(p2.getId())) return false;
                    boolean pd = false;
                    if (p1.getEquipments()!=null&&p2.getEquipments()!=null&&!p1.getEquipments().getGroupId().equals("null")&&!p2.getEquipments().getGroupId().equals("null")){
                        if (p1.getEquipments().getGroupId().equals(p1.getEquipments().getGroupId())){
                            pd=true;
                        }
                    }
                    if (p1.getEmployees()!=null&&p2.getEmployees()!=null){
                        for (int i=0;i<p1.getEmployees().getEmployeeGroups().size();i++){
                            for (int j=0;j<p2.getEmployees().getEmployeeGroups().size();j++){
                                if (p1.getEmployees().getEmployeeGroups().get(i).getAmount() == 0) continue;
                                if (p2.getEmployees().getEmployeeGroups().get(j).getAmount() == 0) continue;
                                if (p1.getEmployees().getEmployeeGroups().get(i).getResourceId().equals(p2.getEmployees().getEmployeeGroups().get(j).getResourceId())){
                                    pd = true;
                                    break;
                                }
                            }
                            if (pd){
                                break;
                            }
                        }
                    }
                    if (pd){//首先排除不交叉的情况
                        if (p1.getStartTimeslot() + LIMIT - 1 < p2.getStartTimeslot()){
                            return false;
                        }
                        if (p2.getStartTimeslot() + LIMIT - 1 < p1.getStartTimeslot()){
                            return false;
                        }
                        //剩余情况即为交叉
                        return true;
                    }else{
                        return false;
                    }

                })
                .penalize("ResourceTimeConflict",HardSoftScore.ONE_HARD);
    }

    //一个Order切片必须持有 连续的1H, 新的EndTime计算方式已经实现
    private Constraint SerialTimeConflict(ConstraintFactory constraintFactory) {
        return constraintFactory.from(OrderSlice.class).filter((p1) -> (p1.getStartTimeslot() + LIMIT - 1 - p1.getStartTimeslot() + 1 != LIMIT))
                .penalize("SerialTimeConflict", HardSoftScore.ONE_HARD);
    }


    private Constraint OutOfTimeConflict(ConstraintFactory constraintFactory) {
        return constraintFactory.from(OrderSlice.class).filter((p1) -> {
                        LocalDateTime ddl = p1.getOrder().getDeadline();
                        LocalDateTime finishTime = p1.getScheduleStartTime().plusSeconds(p1.getStartTimeslot()*60*60*12);
                        if (p1.isLastItem() && p1.isLastCraft()){
                            return !ddl.isBefore(finishTime);
                        }else{
                            return false;
                        }
                    }
                ).reward("NotOutOfTime",HardSoftScore.ONE_SOFT);
    }
}